Brandi 인턴쉽(3차 프로젝트)

Brandi 인턴쉽 관리자 클론 Project

1. 프로젝트 소개

Brandi 관리자 클론 (4주 1/28 ~ 2/21)

2. 사용한 기술

HTML/JavaSciript/React/ReactHooks/styled-compomnent/redux

추가 툴

Material-UI/CKEditor/Crypto.js/Webpack build

3. 나의 역할

  • 로그인 회원가입 페이지
  • 셀러 정보 페이지
  • 셀러 목록 페이지
  • 상품 등록 에디터

4. 프로젝트를 진행하며 잘했다고 느껴지는 부분

  • 처음 접해보는 툴들을 학습하고 활용하며 구현하였다.
  • 백엔드 데이터베이스 모델링에 참여하여 함께 상의하고 흐름을 이해하였다.
  • CRA로 빌드하는것이 아닌 Webpack을 활용하여 빌드하며 과정을 학습하였다.

5. 프로젝트를 진행하며 나온 문제들

5.1 로그인, 회원가입 페이지

로그인 및 회원가입 페이지를 작업하면서 아이디 저장기능이 있었는데, 아이디 저장 클릭 시 암호화를 하여 브라우저에 저장을 하고 복호화를 하여 다시 정보를 가져왔다. 암호화 개념이 제대로 잡혀있지않아서 어려움을 겪었다.

const LoginForm = ({ history, handleClick, toSignUp }) => {
  const [checkbox, setCheckbox] = useState(false);
  const [idState, setId] = useState('');
  const [failMsg, setFailMsg] = useState(false);
  const classes = useStyles();

  useEffect(() => {
    const id = localStorage.getItem('saved_id');
    if (id) {
      const decryptedId = CryptoJS.AES.decrypt(id, '해시값');
      const resolveId = decryptedId.toString(CryptoJS.enc.Utf8);
      setId(resolveId);
      setCheckbox(true);
    }
  }, []);

크립토js를 사용하여 양방향 암호화를 했는데 로그인시 체크박스가 체크되어있으면 로컬스토리지에 해시값을 붙혀 암호화를 한 후 보내서 저장하고 새로 렌더링시 복호화를 하여 로그인 입력창에 아이디값이 렌더링되도록 구현 하였다.

회원가입 부분 각 인풋별 유효성검사가 들어가는데 입력할때마다 체크를 해주고 가입버튼 클릭시에도 체크를 해주는 것이 잘 풀리지 않았다.

const validators = {
    id: value =>
      !checkId(value) &&
      '아이디는 5~20글자의 영문, 숫자, 언더바, 하이픈만 사용 가능하며 시작 문자는 영문 또는 숫자입니다.',
    password: value =>
      !(value.length > 3) && '비밀번호의 최소 길이는 4글자입니다.',
    rePassword: value =>
      !(value === inputState.password) && '비밀번호가 일치하지 않습니다',
    phoneNumber: value => {
      if (toString(value).length === 0) {
        return '필수 입력항목입니다.';
      }
      if (!checkPhoneNumber(value)) {
        return '올바른 정보를 입력해주세요. (ex. 01012345678)';
      }
    },
    sellerName: value => {
      if (!value.length > 0) return '필수 입력항목입니다.';
      if (!checkSellerName(value)) return '올바른 정보를 입력해주세요.';
    },
    sellerNameEng: value => {
      if (!value.length > 0) return '필수 입력항목입니다.';
      if (!checkSellerNameEng(value)) return '올바른 정보를 입력해주세요.';
    },
    csNumber: value => {
      if (!value.length > 0) return '필수 입력항목입니다.';
      if (!(value.length === 10 || value.length === 11))
        return '10자리 또는 11자리 번호를 입력해주세요';
    },
    sellerSite: value =>
      !checkUrl(value) &&
      '올바른 주소를 입력해주세요. (ex. http://www.brandi.co.kr)',
    kakaoId: () => {},
    instaId: () => {},
  };

const [errors, setErrors] = useState([]);

  const setValidationResult = (key, data) => {
    let nextState = errors.filter(error => error.key !== key);

    if (data) {
      nextState = nextState.concat({
        key,
        message: data,
      });
    }
    setErrors(nextState);
  };

  const checkBeforeSubmit = () => {
    const submitErrors = Object.entries(validators).reduce(
      (prev, [key, validator]) => {
        const value = inputState[key];
        const result = validator(value, inputState);

        if (!result) return prev;

        return prev.concat({
          key,
          message: result,
        });
      },
      [],
    );

    setErrors(submitErrors);

유효성 검사를 하는 객체하나를 정의해주면서 값은 체크하는 함수를 넣었다. 가입버튼 클릭시 유효성검사 객체를 순회하며 검사를 한 후 에러상태를 관리하는 배열에 추가해주었다.

에디터 기능

<CKEditor
  config={{
    height: '400',
    filebrowserImageUploadUrl: `${server_url}/product/original_image`,
    toolbarGroups: [
      { name: 'styles' },
      { name: 'colors', groups: ['colors'] },
      { name: 'basicstyles', groups: ['basicstyles', 'cleanup'] },
      { name: 'insert' },
      { name: 'paragraph', groups: ['align'] },
      { name: 'document', groups: ['mode', 'document', 'doctools'] },
    ],
    extraPlugins: 'justify,showblocks, colorbutton',
    colorButton: 'colors',
    removeButtons:
      'Flash,Table,HorizontalRule,Smiley,SpecialChar,PageBreak,Iframe,Superscript,Subscript,Underline,RemoveFormat',
  }}
  data={editor.data}
  onChange={onEditorChange}
  onBeforeLoad={CKEDITOR => (CKEDITOR.disableAutoInline = true)}
>
  <label>
    <textarea defaultValue={editor.data} onChange={onChange} />
  </label>
</CKEditor>

상품 등록부분 에디터는 CKEditor를 사용하였는데 사용자가 필요한 기능만 사용할 수 있게 옵션들을 커스터마이징 해주었다. 입력값을 상태관리하며 백엔드에서 longtext로 받을 수 있게 데이터를 html태그로 보내주며 이미지 업로드는 백앤드서버로 전송 후 s3에 저장한뒤 url을 응답으로 주면 응답을 받고 등록을 하여 img태그로 변환시킨다.

기록하고 싶은 코드

const takeStartDate = () => {
  const year = startingDateTime.getFullYear().toString()
  const month = (startingDateTime.getMonth() + 1).toString()
  const date = startingDateTime.getDate().toString()
  return year + '-' + month + '-' + date
}

const takeEndDate = () => {
  const year = endingDateTime.getFullYear().toString()
  const month = (endingDateTime.getMonth() + 1).toString()
  const date = endingDateTime.getDate().toString()
  return year + '-' + month + '-' + date
}

const checkType = type => {
  if (type === '전체') return ''
  if (type === '쇼핑몰') return '&seller_types_id=1;'
  if (type === '마켓') return '&seller_types_id=2'
  if (type === '로드샵') return '&seller_types_id=3'
  if (type === '디자이너브랜드') return '&seller_types_id=4'
  if (type === '제너럴브랜드') return '&seller_types_id=5'
  if (type === '내셔널브랜드') return '&seller_types_id=6'
  if (type === '뷰티') return '&seller_types_id=7'
}

const handleSearch = (value, ofs) => {
  setLoading(true)
  console.log(offset.value, takeStartDate())
  axios
    .get(
      `${API_URL}/seller/list?limit=${
        ofs.value
      }&offset=${value}&start_date=${takeStartDate()}&end_date=${takeEndDate()}&account=${sellerId}&name_kr=${krName}&name_en=${enName}&site_url=${siteUrl}&representative_name=${managerName}&mobile_number=${managerNumber}&email=${managerMail}${checkType(
        sellerProp
      )}&id=${number}`
    )
    .then(res => {
      console.log(res.data)
      setSellerData(res.data.seller_info)
      setTotal(res.data.seller_count)
      console.log(res.data.seller_info)
      setLoading(false)
    })
    .catch(err => {
      alert('올바르지 않은 요청입니다')
      console.log(err)
      location.reload()
      // setLoading(false);
    })
}

셀러 검색부분 쿼리스트링 요청 부분을 구현하는데 검색옵션이 많고 페이지네이션도 처리해야 했기에 쉽지않은 부분이었다. 각 옵션별 타입을 백엔드와 맞추며 의사소통하고 그 과정에서 문제들을 해결해나가면서 어렵지만 재미를 느낀 부분이었다.


Written by@jaeyoung-son
배운것을 기록하는 공간입니다.

GitHub